﻿//-----------------------------------------------------------------------
// <copyright company="www.3WayWebdesign.de">
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
//
// <author>Michael Morbach</author>
// <email>Michael@3waywebdesign.de</email>
// <web>http://www.3waywebdesign.de</web>
//-----------------------------------------------------------------------

namespace Tww.IBAN.IBANUtilities.Converter
{
    #region Imports

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Tww.IBAN.IBANUtilities.Constants;
    using Tww.IBAN.IBANUtilities.Model;
    using Tww.IBAN.IBANUtilities.Parser;


    #endregion

    public class DefaultIBANConverter : IIBANConverter
    {
        #region IIBANConverter Implementation

        /// <summary>
        /// Gets the IBAN as a trimmed string like it is used in electronic form
        /// </summary>
        /// <param name="iban">IBAN Object to convert</param>
        /// <returns>IBAN as string ( e.g. -> DE21200500000123456000 )</returns>
        public string ConvertToElectronicIBAN( Model.IBANModel iban )
        {
            var structure = CountryCodes.CountryIBANStructes.SingleOrDefault( c => c.CountryShortCode.ToLower().Trim() == iban.Country.ToLower().Trim() );

            if ( structure != null )
            {
                var ibanStructure = CharTransformationChart.NormalizeIBAN( structure.IBANStructure );
                var ibanBuilder = new StringBuilder();
                int countryCharCount = iban.Country.Length;
                int bankcodeCharCount = iban.BankCode.Length;
                int branchCharCount = iban.Branch.Length;
                int accountCharCount = iban.AccountNumber.Length;
                int miscCharCount = iban.Miscellaneous.Length;

                // IBAN is build from right to left. That's why the new iban is build from left to right and then mirrored
                for ( int i = ibanStructure.Length - 1; i >= 0; i-- )
                {
                    var currentStructureChar = ibanStructure[ i ];
                    switch ( currentStructureChar )
                    {
                        // Country
                        case 'c':
                            ibanBuilder.Append( ( countryCharCount-- <= 0 ) ? '0' : char.ToUpper( iban.Country[ countryCharCount ] ) );
                            break;

                        // checksum
                        case 'p':
                            ibanBuilder.Append( '$' );
                            break;

                        // bankcode
                        case 'b':
                            ibanBuilder.Append( ( bankcodeCharCount-- <= 0 ) ? '0' : char.ToUpper( iban.BankCode[ bankcodeCharCount ] ) );
                            break;

                        // branch
                        case 'r':
                            ibanBuilder.Append( ( branchCharCount-- <= 0 ) ? '0' : char.ToUpper( iban.Branch[ branchCharCount ] ) );
                            break;

                        // account
                        case 'a':
                            ibanBuilder.Append( ( accountCharCount-- <= 0 ) ? '0' : char.ToUpper( iban.AccountNumber[ accountCharCount ] ) );
                            break;

                        // misc
                        case 'i':
                            ibanBuilder.Append( ( miscCharCount-- <= 0 ) ? '0' : char.ToUpper( iban.Miscellaneous[ miscCharCount ] ) );
                            break;
                    }
                }

                // mirror the iban
                var generatedIBAN = String.Concat( ibanBuilder.ToString().Reverse() );
                return generatedIBAN.Replace( "$$", this.CalculateChecksum( generatedIBAN ).ToString().PadLeft( 2, '0' ) );
            }
            else
            {
                throw new ArgumentException( string.Format( "The countrycode of the provided IBAN is not recognized - Code {0}", iban.Country ) );
            }
        }

        /// <summary>
        /// Gets the IBAN as a four block string like it is used in paper form
        /// </summary>
        /// <param name="iban">IBAN Object to convert</param>
        /// <returns>IBAN as string ( e.g. -> IBAN DE21 2005 0000 0123 4560 00 )</returns>
        public string ConvertToFourBlockIBAN( Model.IBANModel iban )
        {
            var electronicIBAN = this.ConvertToElectronicIBAN( iban );
            var stringbuilder = new StringBuilder();

            int charCount = 0;
            foreach ( var character in electronicIBAN )
            {
                stringbuilder.Append( character );
                charCount++;

                // Insert a block every 4 chars
                if ( charCount % 4 == 0 )
                {
                    stringbuilder.Append( " " );
                }
            }

            return stringbuilder.ToString();
        }

        /// <summary>
        /// Parses a string and builds a new IBANModel
        /// </summary>
        /// <param name="ibanParser">A Parser that reads the inputstring</param>
        /// <param name="ibanAsString">The Inputstring that represents the IBAN</param>
        /// <returns>New IBANModel</returns>
        public Model.IBANModel ConvertToIBANModel( Parser.IIBANParser ibanParser, string ibanAsString )
        {
            return ibanParser.Parse( ibanAsString );
        }

        /// <summary>
        /// Parses a string a builds a new IBANModel
        /// </summary>
        /// <param name="ibanAsString">The Inputstring that represents the IBAN</param>
        /// <returns>New IBANModel</returns>
        public IBANModel ConvertToIBANModel( string ibanAsString )
        {
            return this.ConvertToIBANModel( new DefaultIBANParser(), ibanAsString );
        }

        /// <summary>
        /// Gets the Checksum of this IBAN Object
        /// </summary>
        /// <param name="iban">IBAN Object to check</param>
        /// <returns>Checksum</returns>
        public int CalculateChecksum( string iban )
        {
            var checksumiban = string.Format( "{0}{1}00", String.Concat( iban.Skip( 4 ).Take( iban.Length - 4 ) ), String.Concat( iban.Take( 2 ) ) );
            checksumiban = CharTransformationChart.TransformToString( checksumiban );

            do
            {
                checksumiban = string.Format( "{0}{1}", int.Parse( String.Concat( checksumiban.Take( 9 ) ) ) % 97, String.Concat( checksumiban.Skip( 9 ) ) );
            }
            while ( checksumiban.Length >= 3 );

            return 98 - int.Parse( checksumiban );
        }

        #endregion






    }
}
